home *** CD-ROM | disk | FTP | other *** search
-
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <dos/rdargs.h>
- #include <dos/stdio.h>
- #include <proto/dos.h>
- #include <proto/utility.h>
- #include "util.h"
-
- #define NODEBUG 1
-
- #ifdef DEBUG
- # define Dputs puts
- #else
- # define Dputs(x)
- #endif
-
- /*
- Now the template to collect all arguments of IF/ELIF/ELSEIF. They
- are the same as for Amiga IF, with these exceptions:
-
- ARGS/M - here I collect everything which is not an argument
- to an option or a flag. eg. the line
-
- IF str1 EQ str2
-
- will yield "str2" as the argument of EQ and "str1"
- will be the first thing in ARGS.
- [/S - This is a trick. By making [ a flag, I can ask for it
- and need not check ARGS.
- */
- const char arg_template[] = "ARGS/M,NOT/S,WARN/S,ERROR/S,FAIL/S,EQ/K,GT/K,\
- GE/K,VAL/S,EXISTS/K,[/S";
-
- /*
- This is the structure for the arguments to IF. I don't use
- an LONG[]-Array since this way, it's much easier and error-prone
- to lookup an argument (and I need only ONE cast when I call ReadArgs()
- and not every time when I access the argument.
- */
- struct IfArgs
- {
- char ** args;
- ULONG not;
- ULONG warn;
- ULONG error;
- ULONG fail;
- char * eq;
- char * gt;
- char * ge;
- ULONG val;
- char * exists;
- ULONG test;
- };
-
- /*
- This is the second template. It is used if the condition of IF doesn't
- hold. Then I continue reading input (thus effectively read the next
- line of input). The input is split up into garbage (REST) and a
- couple of flags: IF, ELSE, ENDIF, FI, ELIF, ELSEIF. If I find IF,
- I'll note that the next ELSE/ENDIF is meaningless and whenever
- I find an ENDIF/FI, I check if it's the matching one for my IF.
- See below how to continue reading input. Note that I still need
- more commands (ENDIF, ELSE, etc.) since there can be occasions when the
- shell sees them (eg. when I skip the THEN body to look up the optional
- ELSE. When the ELSE branch is executed, the shell will eventually
- hit the ENDIF).
- */
- const char cont_template[] = "REST/M,IF/S,ELSE/S,ENDIF/S,FI/S,ELIF/S,ELSEIF/S";
-
- /* This structure is used to filter the body of the IF. */
- struct ContStruct
- {
- char ** rest;
- ULONG is_if;
- ULONG is_else;
- ULONG is_endif;
- ULONG is_fi;
- ULONG is_elif;
- ULONG is_elseif;
- };
-
- /* Prototypes */
- int test_amiga (struct IfArgs *);
- int test_unix (struct IfArgs *);
- int exists (char *);
-
- /*
- Main function. argc and argv are ignored since ReadArgs() cooks the
- parameters for me.
- */
- int main (int argc, char ** argv)
- {
- struct IfArgs arg_struct; /* The calling parameters */
- struct ContStruct cont_struct; /* The contents of a line of the
- stuff following the IF */
- struct RDArgs * rda, /* Reader for the Arguments to IF */
- * cont_rda; /* Reader for the stuff following
- the IF (see below) */
- int level; /* Nesting level for IFs */
- BPTR old_in, /* Original value of Input() */
- current_input; /* *magic* (See below) */
- int cond; /* The result of the evaluation of
- the IF condition. */
-
- /* Clear my args */
- memset (&arg_struct, 0, sizeof(arg_struct));
-
- /* Analyze args */
- if ( (rda = ReadArgs ((char *)arg_template, (LONG *)&arg_struct, NULL)) )
- {
- /* If I have any Amiga-Keywords, I evaluate them Amiga-like */
- if (arg_struct.not || arg_struct.warn || arg_struct.error ||
- arg_struct.fail || arg_struct.val || arg_struct.eq ||
- arg_struct.gt || arg_struct.ge || arg_struct.exists)
- {
- cond = test_amiga (&arg_struct);
- }
- else /* Otherwise, we try the UN*X bourne shell version */
- {
- cond = test_unix (&arg_struct);
- }
-
- #ifdef DEBUG
- printf ("IF = %s\n", cond ? "TRUE" : "FALSE");
- #endif
-
- /*
- Here is the magic part. If the condition is FALSE, we must
- continue execution after the next ELSE or ENDIF (FI, ELIF,
- etc.). How do we do that ? In fact, it's pretty simple: You can
- continue reading input ! That input doesn't come from stdin,
- though, but from cli->cli_CurrentInput. This stream points into
- the script OR interactive shell where the line came from that
- called us. So I switch stdin to cli_CurrentInput, read until
- the next entry point is found and then I exit - the stream
- handle will then point to the next valid command and the shell
- will happily continue execution. The same happens when an ELSE
- is found. ELSE is just more simple since no condition must be
- evaluated.
- */
- if (!cond)
- {
- LONG current_pos;
- int c;
-
- level = 0; /* No nesting if IF/ELSE/ENDIF's yet */
-
- /*
- now I fetch the BPTRs to stdin and CurrentInput to swap
- them. Later, when the input has been processed, I'll
- undo that.
- */
- current_input = Cli()->cli_CurrentInput;
- old_in = Input ();
-
- /* Set new input stream */
- SelectInput (current_input);
-
- /*
- This is heavy magic. ReadArgs() needs and buffered
- stream and the FGetC()/Flush()-Pair provides for that.
- The UnGetC() puts the character back we read.
- */
- c = FGetC (current_input);
- Flush (current_input);
- UnGetC (current_input, c);
-
- for (;;)
- {
- /*
- Remember the current position in case we find an
- ELSEIF. When we hit an ELSEIF, we seek back to the
- beginning of the line and exit. Then the shell will see
- the ELSEIF as the next command and continue with it.
- */
- current_pos = Seek (current_input, 0, OFFSET_CURRENT);
-
- /* Clear the argument structure */
- memset (&cont_struct, 0, sizeof(cont_struct));
-
- /* Read a line of input */
- if (!(cont_rda = ReadArgs ((char *)cont_template,
- (LONG *)&cont_struct, NULL)) )
- {
- PrintFault (IoErr(), "ContReadArgs");
- cond = 10;
- break;
- }
-
- /* now look what we got. */
- if (cont_struct.is_if)
- {
- /* If it's an IF..., we increase the nesting level. */
- level ++;
- }
- else if (cont_struct.is_endif || cont_struct.is_fi ||
- cont_struct.is_else)
- {
- /*
- The IF has ended (either bei ENDIF or ELSE).
- We can handle ELSE here, too since the condition
- was FALSE and thus we have to execute the
- commands in the ELSE branch. Also we stop only
- if level is 0 to allow nesting.
- */
- if (!level)
- break;
-
- level --;
- }
- else if (cont_struct.is_elif || cont_struct.is_elseif)
- {
- /*
- ELSEIF found. Seek back and continue with the
- beginning of the line.
- */
- if (!level)
- {
- if (Seek (current_input, current_pos,
- OFFSET_BEGINNING) == -1)
- {
- PrintFault (IoErr(), "Seek back");
- cond = 10;
- }
- break;
- }
- }
- /*
- If nothing interesting was found, we just ignore it
- (that are the lines which make up the THEN branch.
- */
-
- /* Free memory and read the next line */
- FreeArgs (cont_rda);
- } /* for */
-
- /* No more input but nesting level != 0 ? Something's wrong. */
- if (level)
- {
- fprintf (stderr, "Missing ELSE or ENDIF");
- cond = 10;
- }
-
- /* Undo magic */
- SelectInput (old_in);
-
- /*
- If we had to abort the loop, we might have to free
- memory here.
- */
- if (cont_rda)
- FreeArgs (cont_rda);
- }
-
- /* Free original arguments */
- FreeArgs (rda);
- }
- else
- {
- PrintFault (IoErr(), "ReadArgs() Error");
- cond = 10;
- }
-
- /* Return error code (1 as "IF TRUE" must be filtered out) */
- return (cond < 5) ? 0 : cond;
- } /* main */
-
- /* Check if a file exists */
- int exists (char * fname)
- {
- BPTR lock;
- int cond;
-
- lock = Lock (fname, SHARED_LOCK);
-
- if ( (cond = (lock != NULL)) )
- UnLock (lock);
-
- return cond;
- } /* exists */
-
- /* Compare the dates of two files. The result will be
-
- -1 if fn1 is older than fn2
- 0 if fn1 and fn2 have the same date
- 1 if fn1 is newer than fn2
- 10 if an error occurred. In this case, a message has been
- printed.
- */
- int compare_dates (char * fn1, char * fn2)
- {
- int cond = 10;
- __aligned struct FileInfoBlock fib1, fib2;
- BPTR lock1, lock2;
-
- /* 1. Lock both files */
- lock1 = Lock (fn1, SHARED_LOCK);
-
- if (lock1)
- {
- lock2 = Lock (fn2, SHARED_LOCK);
-
- if (!lock2)
- PrintFault (IoErr(), fn2);
- }
- else
- PrintFault (IoErr(), fn1);
-
- /* 2. If that worked, Examine() the files */
- if (lock1 && lock2)
- {
- if (!Examine (lock1, &fib1))
- {
- PrintFault (IoErr(), fn1);
- }
- else if (!Examine (lock2, &fib2))
- {
- PrintFault (IoErr(), fn2);
- }
- else
- {
- /*
- Compare the dates and truncate the result (CompareDates()
- can return -15 and the like)
- */
- cond = CompareDates (&fib2.fib_Date, &fib1.fib_Date);
-
- if (cond < 0)
- cond = -1;
- else if (cond > 0)
- cond = 1;
- }
- }
-
- /* 3. Release the files */
- if (lock1)
- {
- UnLock (lock1);
-
- if (lock2)
- UnLock (lock2);
- }
-
- /* Return the result */
- return cond;
- } /* compare_dates */
-
- /* Evaluate a standard Amiga IF */
- int test_amiga (struct IfArgs * args)
- {
- int cond;
-
- /* Case 1: Check Result */
- if (args->warn || args->error || args->fail)
- {
- char buffer[32];
- int level;
-
- /* Get the result of the last command */
- GetVar ("RC", buffer, sizeof (buffer), GVF_LOCAL_ONLY);
-
- /* Choose a level */
- if (args->warn)
- level = 5;
- else if (args->error)
- level = 10;
- else
- level = 20;
-
- /* Compare result and the level */
- cond = atoi(buffer) >= level;
- }
- else if (args->exists) /* IF EXISTS */
- {
- cond = exists (args->exists);
- }
- else
- {
- if (args->val) /* Numeric compare: val1 ?? val2 */
- {
- int val1, val2;
-
- if (!args->args)
- {
- fprintf (stderr, "Missing second argument");
- return 10;
- }
-
- /* Note that the FIRST figure is in args[] ! */
- val1 = atoi (args->args[0]);
-
- if (args->eq)
- {
- val2 = atoi (args->eq);
-
- cond = val1 == val2;
- }
- else if (args->gt)
- {
- val2 = atoi (args->gt);
-
- cond = val1 > val2;
- }
- else if (args->ge)
- {
- val2 = atoi (args->ge);
-
- cond = val1 >= val2;
- }
- else
- {
- fprintf (stderr, "Missing arguments");
- return 10;
- }
- }
- else /* String compare: str1 ?? str2*/
- {
- char * str1, * str2;
-
- if (!args->args)
- {
- fprintf (stderr, "Missing second argument");
- return 10;
- }
-
- /* Note that the FIRST string is in args[] ! */
- str1 = args->args[0];
-
- if (args->eq)
- {
- str2 = args->eq;
-
- cond = Stricmp (str1, str2) == 0;
- }
- else if (args->gt)
- {
- str2 = args->gt;
-
- cond = Stricmp (str1, str2) > 0;
- }
- else if (args->ge)
- {
- str2 = args->ge;
-
- cond = Stricmp (str1, str2) >= 0;
- }
- else
- {
- fprintf (stderr, "Missing arguments\n");
- return 10;
- }
- }
- }
-
- /* Return result (negated if neccessary */
- return args->not ? !cond : cond;
- } /* test_amiga */
-
- int test (char *** args)
- {
- char ** arg = *args;
- int cond = -1;
- int not = 0;
- int found_string = FALSE;
- int stop = 0;
-
- while (*arg && cond < 5 && !stop)
- {
- if ((*arg)[0] == '(' && !(*arg)[1])
- {
- arg ++;
- cond = test (&arg);
-
- if (cond < 0)
- {
- fprintf (stderr, "IF: Missing arguments in ( EXPRESSSION )\n");
- cond = 10;
- }
- }
- else if ((*arg)[0] == ')' && !(*arg)[1])
- {
- arg ++;
- break;
- }
- else if ((*arg)[0] == ']' && !(*arg)[1])
- {
- if (arg[1] && !((*arg)[0]==';' && !(*arg)[1]))
- {
- fprintf (stderr, "IF: Unexpected arguments after ]\n");
- cond = 10;
- }
-
- break;
- }
- else if ((*arg)[0] == '!')
- {
- if (!(*arg)[1])
- {
- not = !not;
- arg ++;
- }
- else if ((*arg)[1] == '=')
- {
- if (arg[1] && found_string)
- {
- cond = strcmp (arg[-1], arg[1]) != 0;
- arg += 2;
- }
- else
- {
- if (found_string)
- {
- fprintf (stderr, "IF: Missing second argument to !=\n");
- cond = 10;
- }
- else
- {
- fprintf (stderr, "IF: Missing first argument to !=\n");
- cond = 10;
- }
- }
- }
- else
- {
- fprintf (stderr, "IF: Unknown argument %s\n", *arg);
- cond = 10;
- }
- }
- else if ((*arg)[0] == '=' && !(*arg)[1])
- {
- if (arg[1] && found_string)
- {
- cond = strcmp (arg[-1], arg[1]) == 0;
- arg += 2;
- }
- else
- {
- if (found_string)
- {
- fprintf (stderr, "IF: Missing second argument to =\n");
- cond = 10;
- }
- else
- {
- fprintf (stderr, "IF: Missing first argument to =\n");
- cond = 10;
- }
- }
- }
- else if ((*arg)[0] == '-')
- {
- char * ptr;
-
- ptr = *arg+1;
-
- if (!arg[1])
- {
- fprintf (stderr, "IF: Missing argument to %s\n", *arg);
- cond = 10;
- }
- else
- {
- switch (*ptr)
- {
- case 'a': /* AND */
- if (cond == -1)
- {
- fprintf (stderr, "IF: Missing first argument to -a\n");
- cond = 10;
- }
- else
- {
- if (!cond)
- stop = 1;
- arg ++;
- }
- break;
-
- case 'o': /* OR or OLDER_THAN (ot) */
- if (!ptr[1]) /* -o */
- {
- if (cond == -1)
- {
- fprintf (stderr, "IF: Missing first argument to -o\n");
- cond = 10;
- }
- else
- {
- if (cond)
- stop = 1;
- arg ++;
- }
- }
- else
- {
- /* Older than */
- if (!found_string)
- {
- fprintf (stderr, "IF: Missing first argument to -ot\n");
- cond = 10;
- }
- else
- {
- cond = compare_dates (arg[-1], arg[1]);
-
- if (cond != 10)
- cond = cond < 0;
-
- arg += 2;
- }
- }
- break;
-
- case 'n': /* "String has non-zero length" or "Not Equal" (ne)
- or "File1 is newer than File2" (nt) */
- ptr ++;
- switch (*ptr)
- {
- case 0:
- cond = arg[1][0]; /* -n */
- arg += 2;
- break;
-
- case 'e': /* -ne */
- if (found_string)
- {
- cond = atoi(arg[-1]) != atoi(arg[1]);
- arg += 2;
- }
- else
- {
- fprintf (stderr, "IF: Missing first argument to -ne\n");
- cond = 10;
- }
- break;
-
- case 't': /* -nt */
- /* Newer than */
- if (found_string)
- {
- cond = compare_dates (arg[-1], arg[1]);
- arg += 2;
-
- if (cond != 10)
- cond = cond > 0;
- }
- else
- {
- fprintf (stderr, "IF: Missing first argument to -nt\n");
- cond = 10;
- }
- break;
- }
- break;
-
- case 'z': /* "String has zero length" */
- cond = !arg[1][0];
- arg ++;
- break;
-
- case 'e': /* "File exists" or "Files are the same" (ef) or
- "Numbers are equal" (eq) */
- ptr ++;
- switch (*ptr)
- {
- case 0:
- cond = exists (arg[1]);
- arg += 2;
- break;
-
- case 'f': {
- BPTR f1, f2;
-
- if (found_string)
- {
- if (!(f1 = Lock (arg[-1], SHARED_LOCK)) )
- {
- PrintFault (IoErr(), arg[-1]);
- cond = 10;
- }
- else
- {
- if (!(f2 = Lock (arg[1], SHARED_LOCK)) )
- {
- PrintFault (IoErr(), arg[1]);
- cond = 10;
- }
- else
- {
- cond = SameLock (f1, f2) == 0;
- arg += 2;
-
- UnLock (f2);
- }
-
- UnLock (f1);
- }
- }
- else
- {
- fprintf (stderr, "IF: Missing first argument to -ef\n");
- cond = 10;
- }
-
- break; }
- case 'q':
- if (found_string)
- {
- cond = atoi(arg[-1]) == atoi(arg[1]);
- arg += 2;
- }
- else
- {
- fprintf (stderr, "IF: Missing first argument to -eq\n");
- cond = 10;
- }
- break;
- }
- break;
-
- case 'g': /* "I1 >= I2" (ge) or "I1 > I2" (gt) or
- "FILE exists and is set-group-ID" */
- case 'l': /* "I1 <= I2" (le) or "I1 < I2" (lt) */
- {
- int i1, i2;
-
- if (!ptr[1])
- {
- cond = FALSE;
- break;
- }
-
- if (found_string)
- {
- i1 = atoi(arg[-1]);
- i2 = atoi(arg[1]);
- arg += 2;
-
- cond = (*ptr == 'g') ? i1 > i2 : i1 < i2;
-
- if (!cond && ptr[1] == 'e')
- cond = i1 == i2;
- }
- else
- {
- fprintf (stderr, "IF: Missing first argument to %s\n", *arg);
- cond = 10;
- }
- }
- break;
-
- case 't': /* standard output is opened on a terminal */
- cond = IsInteractive (Output ()) != 0;
- arg ++;
- break;
-
- /* The next cases are always false due to AmigaDOS */
- case 'S': /* FILE exists and is a socket */
- case 'b': /* FILE exists and is block special */
- case 'c': /* FILE exists and is character special */
- case 'k': /* FILE exists and has its sticky bit set */
- case 'u': /* FILE exists and its set-user-ID bit is set */
- cond = FALSE;
- arg += 2;
- break;
-
- /* The next cases are always true due to AmigaDOS */
- case 'G': /* File exists and is owned by the effective group ID */
- case 'O': /* FILE exists and is owned by the effective user ID */
- cond = TRUE;
- arg += 2;
- break;
-
- case 'L': /* FILE exists and is a symbolic link */
- case 'p': /* FILE exists and is a named pipe */
- case 'd': /* FILE exists and is a directory */
- case 'f': /* FILE exists and is a regular file */
- case 's': /* FILE exists and has a size greater than zero */
- case 'r': /* FILE exists and is readable */
- case 'w': /* FILE exists and is writable */
- case 'x': /* FILE exists and is executable */
- {
- __aligned struct FileInfoBlock fib;
- BPTR lock;
-
- lock = Lock (arg[1], SHARED_LOCK);
-
- if (lock)
- {
- if (Examine (lock, &fib))
- {
- switch (*ptr)
- {
- case 'L': /* symbolic link */
- cond = fib.fib_DirEntryType == ST_LINKFILE ||
- fib.fib_DirEntryType == ST_LINKDIR ||
- fib.fib_DirEntryType == ST_SOFTLINK;
- break;
-
- case 'p': /* named pipe */
- cond = fib.fib_DirEntryType == ST_PIPEFILE;
- break;
-
- case 'd': /* directory */
- cond = fib.fib_DirEntryType == ST_ROOT ||
- fib.fib_DirEntryType == ST_USERDIR;
- break;
-
- case 'f': /* regular file */
- cond = fib.fib_DirEntryType == ST_FILE;
- break;
-
- case 's': /* size greater than zero */
- cond = fib.fib_DirEntryType == ST_FILE &&
- fib.fib_Size;
- break;
-
- case 'r': /* readable */
- cond = !(fib.fib_Protection & FIBF_READ);
- break;
-
- case 'w': /* writable */
- cond = !(fib.fib_Protection & FIBF_WRITE);
- break;
-
- case 'x': /* executable */
- cond = !(fib.fib_Protection & FIBF_EXECUTE) ||
- (fib.fib_Protection & FIBF_SCRIPT);
- break;
- } /* switch */
- }
- else
- {
- fprintf (stderr, "IF TEST ... %s %s:", arg[0],
- arg[1]);
- PrintFault (IoErr(), NULL);
- cond = 10;
- }
-
- UnLock (lock);
- }
- else
- cond = FALSE;
-
- arg += 2;
- } /* case */
-
- } /* switch */
- } /* if */
- found_string = FALSE;
- }
- else if ((*arg)[0])
- {
- cond = 1;
- if (!found_string)
- found_string = TRUE;
- else
- {
- fprintf (stderr, "IF TEST: Unexpected argument \"%s\"\n",
- *arg);
- cond = 10;
- }
- arg ++;
- }
- else
- {
- fprintf (stderr, "IF TEST: Unexpected empty argument\n");
- cond = 10;
- }
- }
-
- /* If we terminated early, skip until a ) or ] */
- if (stop)
- {
- while (*arg
- && !((*arg)[0] == ')' && !(*arg)[1])
- && !((*arg)[0] == ']' && !(*arg)[1])
- )
- {
- arg ++;
- }
- }
-
- *args = arg;
-
- if (cond == -1)
- {
- fprintf (stderr, "IF TEST: Missing arguments\n");
- cond = 10;
- }
-
- if (cond > 5)
- return cond;
-
- return not ? !cond : cond;
- } /* test */
-
- int test_unix (struct IfArgs * args)
- {
- char ** arg = args->args;
-
- if (!arg)
- {
- fprintf (stderr, "Missing arguments for UN*X-like IF\n");
- return 10;
- }
-
- if (args->test) /* if test is set, implicitly run TEST */
- {
- return test (&arg);
- }
- else /* run all arguments as a command and examine the result */
- {
- int t;
-
- for (t=0; arg[t]; t++)
- {
- if (arg[t][0] == ';' && !arg[t][1])
- break;
- }
-
- return execute_command (t, arg) == 0;
- }
-
- return 0;
- } /* test_unix */
-
-